package com.tws.plugin.servicemanager;

import java.lang.reflect.Proxy;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;

import com.tws.plugin.servicemanager.compat.BundleCompat;
import com.tws.plugin.servicemanager.local.ServicePool;

/**
 * @author yongchen
 * 
 * 利用ContentProvider实现同步跨进程调用，如果contentprovider所在进程退出，
 * 其他服务进程注册的binder和service信息会丢失
 */
public class ServiceProvider extends ContentProvider {

	public static final String REPORT_BINDER = "report_binder";
	public static final String PUBLISH_SERVICE = "publish_service";
	public static final String PUBLISH_SERVICE_BINDER = "publish_service_binder";
	public static final String UNPUBLISH_SERVICE = "unpublish_service";
	public static final String CALL_SERVICE = "call_service";
	public static final String QUERY_SERVICE = "query_service";
	public static final String QUERY_SERVICE_RESULT_IS_IN_PROVIDIDER_PROCESS = "query_service_result_is_in_provider_process";
	public static final String QUERY_SERVICE_RESULT_BINDER = "query_service_result_binder";
	public static final String QUERY_SERVICE_RESULT_DESCRIPTOR = "query_service_result_desciptor";
	public static final String QUERY_INTERFACE = "query_interface";
	public static final String QUERY_INTERFACE_RESULT = "query_interface_result";

	public static final String PID = "pid";
	public static final String BINDER = "binder";
	public static final String NAME = "name";
	public static final String INTERFACE = "interface";

	private static Uri CONTENT_URI;

	// 服务名：进程ID
	private static ConcurrentHashMap<String, Recorder> allServiceList = new ConcurrentHashMap<String, Recorder>();
	// 进程ID：进程Binder
	private static ConcurrentHashMap<Integer, IBinder> processBinder = new ConcurrentHashMap<Integer, IBinder>();

	public static Uri buildUri() {
		if (CONTENT_URI == null) {
			CONTENT_URI = Uri.parse("content://" + ServiceManager.sApplication.getPackageName() + ".svcmgr/call");
		}
		return CONTENT_URI;
	}

	@Override
	public Bundle call(String method, String arg, Bundle extras) {

		if (Build.VERSION.SDK_INT >= 19) {
			Log.d("call", "callingPackage = " + getCallingPackage());
		}

		Log.d("call", "Thead : id = " + Thread.currentThread().getId() + ", name = " + Thread.currentThread().getName() + ", method = " + method + ", arg = "
				+ arg);

		if (method.equals(REPORT_BINDER)) {
			final int pid = extras.getInt(PID);
			IBinder iBinder = BundleCompat.getBinder(extras, BINDER);
			processBinder.put(pid, iBinder);
			try {
				iBinder.linkToDeath(new IBinder.DeathRecipient() {
					@Override
					public void binderDied() {
						removeAllRecordorForPid(pid);
					}
				}, 0);
			} catch (RemoteException e) {
				e.printStackTrace();
				processBinder.remove(pid);
			}
		} else if (method.equals(PUBLISH_SERVICE)) {

			String serviceName = arg;
			int pid = extras.getInt(PID);
			String interfaceClass = extras.getString(INTERFACE);
			IBinder binder = processBinder.get(pid);
			if (binder != null && binder.isBinderAlive()) {
				Recorder recorder = new Recorder();
				recorder.pid = pid;
				recorder.interfaceClass = interfaceClass;
				allServiceList.put(serviceName, recorder);
			} else {
				allServiceList.remove(pid);
			}

			return null;

		} else if (method.equals(UNPUBLISH_SERVICE)) {

			int pid = extras.getInt(PID);
			String name = extras.getString(NAME);
			if (TextUtils.isEmpty(name)) {
				removeAllRecordorForPid(pid);
			} else {
				allServiceList.remove(name);
				notifyClient(name);
			}

			return null;
		} else if (method.equals(CALL_SERVICE)) {

			return MethodRouter.routerToInstance(extras);

		} else if (method.equals(QUERY_INTERFACE)) {
			Bundle bundle = new Bundle();
			Recorder recorder = allServiceList.get(arg);
			if (recorder != null) {
				bundle.putString(QUERY_INTERFACE_RESULT, recorder.interfaceClass);
			}
			return bundle;
		} else if (method.equals(QUERY_SERVICE)) {
			String serviceName = arg;
			if (allServiceList.containsKey(serviceName)) {

				Object instance = ServicePool.getService(serviceName);

				Bundle bundle = new Bundle();
				if (instance != null && !Proxy.isProxyClass(instance.getClass())) {
					bundle.putBoolean(QUERY_SERVICE_RESULT_IS_IN_PROVIDIDER_PROCESS, true);
					return bundle;
				} else {
					Recorder recorder = allServiceList.get(serviceName);
					if (recorder != null) {
						IBinder iBinder = processBinder.get(recorder.pid);
						if (iBinder != null && iBinder.isBinderAlive()) {
							bundle.putBoolean(QUERY_SERVICE_RESULT_IS_IN_PROVIDIDER_PROCESS, false);
							bundle.putString(QUERY_SERVICE_RESULT_DESCRIPTOR, ProcessBinder.class.getName() + "_" + recorder.pid);
							BundleCompat.putBinder(bundle, QUERY_SERVICE_RESULT_BINDER, iBinder);
							return bundle;
						}
					}
					return null;
				}
			}

		}
		return null;
	}

	private void removeAllRecordorForPid(int pid) {
		Log.w("ServiceProvider", "remove all service recordor for pid" + pid);

		// 服务提供方进程挂了,或者服务提供方进程主动通知清理服务, 则先清理服务注册表, 再通知所有客户端清理自己的本地缓存
		processBinder.remove(pid);
		Iterator<Map.Entry<String, Recorder>> iterator = allServiceList.entrySet().iterator();
		while (iterator.hasNext()) {
			Map.Entry<String, Recorder> entry = iterator.next();
			if (entry.getValue().pid.equals(pid)) {
				iterator.remove();
				notifyClient(entry.getKey());
			}
		}
	}

	private void notifyClient(String name) {
		// 通知持有服务的客户端清理缓存
		Intent intent = new Intent(ServiceManager.ACTION_SERVICE_DIE_OR_CLEAR);
		intent.putExtra(NAME, name);
		ServiceManager.sApplication.sendBroadcast(intent);
	}

	public static class Recorder {
		public Integer pid;
		public String interfaceClass;
	}

	@Override
	public boolean onCreate() {
		return false;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
		// doNothing
		return null;
	}

	@Override
	public String getType(Uri uri) {
		// doNothing
		return null;
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		// doNothing
		return null;
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		// doNothing
		return 0;
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
		// doNothing
		return 0;
	}

}
